package waal.pc.backend.writer;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;

import waal.pc.backend.Log;
import waal.pc.backend.channels.Channel;
import waal.pc.backend.channels.ChannelDataListener;
import waal.pc.backend.dataPoints.AnalogDataPoint;
import waal.pc.backend.dataPoints.DataPoint;
import waal.pc.backend.hub.Hub;
import waal.pc.backend.module.Module;


public class CSVwriterHub extends Thread implements ChannelDataListener {

	private Hub hub;
	private Map<Channel, Queue<DataPoint>> queues;
	private Set<Channel> channels;
	private boolean running = true;
	private PrintWriter pw = null;
	private FileWriter fw = null;
	private BufferedWriter bw = null;
	private File path = new File("P:\\coffee\\Waaal\\csv\\");
	private SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd_HH-mm-ss");
	private String seperator = ";"; 
	DecimalFormat df = new DecimalFormat("0.00000E00");
	DecimalFormatSymbols dfs = new DecimalFormatSymbols();
	
	
	public CSVwriterHub(Hub hub) {
		this.hub = hub;
		this.queues = new HashMap<Channel, Queue<DataPoint>>();
		this.channels = new TreeSet<Channel>();
		this.dfs.setDecimalSeparator(".".charAt(0));
		this.df.setDecimalFormatSymbols(dfs);
		this.createWriter();
		this.pw.print("time");
		this.fillChannelList();
		//System.out.println("CSW Writer for Hub " + hub.getName()  + " erstellt");
	}
	
	public void run() {
		boolean exitRun = false;
		Iterator<Channel> ic;
		TreeMap<Double,DataPoint[]> time = new TreeMap<Double, DataPoint[]>();;
		Channel c;
		Queue<DataPoint> q;
		double timeValue;
		DataPoint d;
		DataPoint[] ds;
		DataPoint[] dsOld = new DataPoint[this.channels.size()];
		DataPoint[] dsBlank = new DataPoint[this.channels.size()];
		
		Map.Entry<Double, DataPoint[]> me;
		// create a Array with null Elements
		for(int counter = 0; counter < dsBlank.length; counter++) {
			dsBlank[counter] = null;
			dsOld[counter] = new AnalogDataPoint(0,0);
		}
		int i;
		
		// Do in a Loop
		while(this.running) {
			
			// Get the channels
			ic = this.channels.iterator(); 
			i = 0;
			//System.out.print(" CSV Channels");
			while(ic.hasNext()) {
				//System.out.print(" x");
				c = ic.next();
				//System.out.println("Channel: " + c.getChannelNumber());
				q = this.queues.get(c);
				// move all Entries in the queue to time
				// when there is an Entry in the time Map for this timeValue add the DataPoint
				// else create an new Entry for this timestep with the DataPoint
				while((d=q.poll()) != null) {
					//System.out.print(".");
					if(time.containsKey(d.getTime())) { //timevalue exists
						ds = time.get(d.getTime());
						ds[i] = d;
					} else { // the first time that this timevalue occurs
						ds = dsBlank.clone();
						ds[i] = d;
						time.put(d.getTime(), ds);
					}
				}
				i++;
			}
			// As long as there a more than 3 Values in the TimeMap save the values to the file
			//System.out.println();
			//System.out.println("time size: " + time.size());
			
			if(time.size() > 20) {
				while(time.size() > 3) {
					//System.out.println("Save  CSV data");
					me = time.firstEntry();
					ds = me.getValue();
					timeValue = me.getKey();
					time.remove(me.getKey());
					this.pw.print(timeValue);
					for(i = 0; i < ds.length; i++) {
						if(ds[i] == null) {
							ds[i] = dsOld[i];
						}
						this.pw.print(this.seperator + this.df.format(ds[i].getValue()));
					}
					dsOld = ds;
					this.pw.print("\n");
				}
			}
			this.pw.flush();
			
			// Wait for secound()
			if(!exitRun) {
				try {
					TimeUnit.SECONDS.sleep(1);
				} catch (InterruptedException e) {
					// nothing eval can happen
					exitRun = true;
					//System.out.println("Interrupt");
				}
			} else {
				while(time.size() > 0) {
					//System.out.println("Save  CSV data");
					me = time.firstEntry();
					ds = me.getValue();
					timeValue = me.getKey();
					time.remove(me.getKey());
					this.pw.print(df.format(timeValue));
					//Log.addToDebug(3, "CSVwriterHub", "run", "Time: " + timeValue + " Entrys: " + ds.length);
					for(i = 0; i < ds.length; i++) {
						if(ds[i] == null) {
							ds[i] = dsOld[i];
						}
						this.pw.print(this.seperator + this.df.format(ds[i].getValue()));
					}
					dsOld = ds;
					this.pw.print("\n");
				}
				this.pw.flush();
				this.running = false;
				this.closeWriter();
				
			}
		}
	}
	
	public void stopWriter() {
		//System.out.println("Stop CSV");
		this.interrupt();
		for(Iterator<Channel> ic = this.channels.iterator(); ic.hasNext(); ) {
			ic.next().removeChannelDataListener(this);
		}
		
	}
	

	private void fillChannelList() {
		//System.out.println("Channels in Hub: " + this.hub.getChannels().size());
		Iterator<Module> im = this.hub.getModules().iterator();
		Module m;
		Iterator<Channel> ic;
		Channel c;
		while(im.hasNext()) {
			m = im.next();
			if(m.isActive()) {
				Log.addToDebug(3, "CSVwriterHub", "fillChannelListe", "fill ChannelList with module " + m.getNumber() + " channels");
				ic = m.getChannels().iterator();
				while(ic.hasNext()) {
					c = ic.next();
					Log.addToDebug(3, "CSVwriterHub", "fillChannelListe", "add Channel " + c.getChannelNumber() + " from module " + m.getNumber());
					this.channels.add(c);
					this.queues.put(c, c.getMyQueue(this));
					
				}
			}
		}
		
		
		ic = this.channels.iterator();
		while(ic.hasNext()) {
			this.pw.print(this.seperator + ic.next().toString() );
		}
		this.pw.print("\n");
	}
	
	private void createWriter() {
		File f = new File(this.path, this.sdf.format(this.hub.getDate()) + "_" + this.hub.getName() + ".csv");
		try
		{
			this.fw = new FileWriter( f , true);
			this.bw = new BufferedWriter( this.fw );
			this.pw = new PrintWriter( this.bw );
		} catch ( IOException e ) {
			System.err.println( "Error creating file: " + f.toString() );
		}
	}
	
	private void closeWriter() {
		if(this.pw != null) {
			//System.out.println("release File");
			this.pw.close();
			try {
				this.bw.close();
				this.fw.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}
	
	@Override
	public void channelGetKilled(Channel ch) {
		// TODO Auto-generated method stub
		
	}
}
